﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Snowdreamist.Threading;
using SNSpirit.Service.Certificate;

namespace SNSpirit.Service.Authentication
{
    public abstract class AuthenticationServiceBase : IAuthenticationService
    {
        public AuthenticationServiceBase()
        {
            BackgroundJob.Run(new Action(() =>
                {
                    var r = this.GetAuthenticationState();
                    this.OnStateChanged(r.Token, r.Error);
                }));
        }

        public abstract AuthResult Authenticate(ICertificate certificate);

        public virtual void AuthenticateAsync(ICertificate certificate, AuthenticateCallback callback)
        {
            if (certificate == null)
                throw new ArgumentNullException("certificate");
            BackgroundJob.Run(new Action<ICertificate, AuthenticateCallback>(this.AuthenticateWorker), certificate, callback);
        }

        public abstract LogoutResult Logout();

        public virtual void LogoutAsync(LogoutCallback callback)
        {
            BackgroundJob.Run(new Action<LogoutCallback>(this.LogoutWorker), callback);
        }

        public virtual bool IsAuthenticated { get; private set; }

        public virtual AuthenticationToken Token { get; private set; }

        public event EventHandler<AuthenticateStateChangedEventArgs> StateChanged;

        #region protected

        protected abstract AuthResult GetAuthenticationState();

        protected virtual void AuthenticateWorker(ICertificate certificate, AuthenticateCallback callback)
        {
            AuthResult result = null;
            Exception error = null;

            try
            {
                result = this.Authenticate(certificate);
            }
            catch (Exception err)
            {
                error = err;
            }
            finally
            {
                if (result.Error == null && error != null)
                    result.Error = error;
                if (callback != null)
                    callback(result);
            }
        }

        protected virtual void LogoutWorker(LogoutCallback callback)
        {
            LogoutResult result = null;
            Exception error = null;

            try
            {
                result = this.Logout();
            }
            catch (Exception err)
            {
                error = err;
            }
            finally
            {
                if (result.Error == null && error != null)
                    result.Error = error;
                if (callback != null)
                    callback(result);
            }
        }

        protected virtual void OnStateChanged(AuthenticationToken token, Exception error = null)
        {
            this.IsAuthenticated = (token != null && !token.IsAnonymous);
            this.Token = token;
            if (this.StateChanged != null)
                this.StateChanged(this, new AuthenticateStateChangedEventArgs(token, error));
        }

        #endregion protected
    }
}